/*
 * 
 * Libman
 * 
 * Daniel Kaufmann, Martin Lang, Simon Moosbrugger,
 * Stefan Mayer, Stephan Halbeisen
 * 
 */
package application.controller.server;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

import model.User;
import model.dao.DAOFactory;
import model.dao.UserDAO;
import application.exceptions.DatabaseException;
import application.exceptions.SessionException;
import application.exceptions.UserNotInDbException;
import application.exceptions.WrongCredentialsException;

/**
 * The Class SessionController provides Authentication and Sessionchecking
 */
public class SessionController {

	/**
	 * The Class SessionContainer contains information about the current
	 * session.
	 */
	private class SessionContainer {

		/** The _user. */
		private User _user;

		/** The _login. */
		private Date _login;

		/** The _ip. */
		private String _ip;

		/**
		 * Instantiates a new session container.
		 * 
		 * @param user
		 *            the user
		 * @param login
		 *            the login
		 * @param ip
		 *            the ip
		 */
		public SessionContainer(User user, Date login, String ip) {
			_user = user;
			_login = login;
			_ip = ip;
		}

		/**
		 * Sets the user.
		 * 
		 * @param user
		 *            the new user
		 */
		public void setUser(User user) {
			_user = user;
		}

		/**
		 * Gets the user.
		 * 
		 * @return the user
		 */
		public User getUser() {
			return _user;
		}

		/**
		 * Sets the login.
		 * 
		 * @param login
		 *            the new login
		 */
		private void setLogin(Date login) {
			_login = login;
		}

		/**
		 * Gets the login.
		 * 
		 * @return the login
		 */
		private Date getLogin() {
			return _login;
		}

		/**
		 * Sets the ip.
		 * 
		 * @param ip
		 *            the new ip
		 */
		private void setIp(String ip) {
			_ip = ip;
		}

		/**
		 * Gets the ip.
		 * 
		 * @return the ip
		 */
		private String getIp() {
			return _ip;
		}
	}

	/** The sessions. */
	public static HashMap<String, SessionContainer> sessions = new HashMap<String, SessionContainer>();

	/**
	 * Check session.
	 * 
	 * @param session
	 *            the session
	 * @param ip
	 *            the ip
	 * @return the role of the user
	 * @throws SessionException
	 *             the session exception
	 */
	public String checkSession(String session, String ip)
			throws SessionException {
		SessionContainer sc = null;
		if (sessions.containsKey(session)) {
			sc = sessions.get(session);
		} else {
			// Session is unknown
			throw new SessionException(
					new SessionException.SessionDoesNotExistException());
		}
		
//		if (!ip.equals(sc.getIp())) {
//			/*
//			 * Ip is different to the one which was detected at login Used for
//			 * prevent sniffing sessionID and calling methods from another
//			 * client
//			 */
//			throw new SessionException(
//					new SessionException.InvalidIpException());
//		}

		if (isSessionExpired(sc.getLogin())) {
			sessions.remove(session);
			throw new SessionException(
					new SessionException.SessionExpiredException());
		}

		return sc.getUser().getRole().getName();
	}

	/**
	 * Login
	 * Authentication is realized with the FHV OpenLDAP server
	 * ldaps://ldap.fhv.at:636
	 * Userdn: uid={username},ou=fhv,ou=People,dc=uclv,dc=net
	 * 
	 * @param username
	 *            the username
	 * @param password
	 *            the password
	 * @param ip
	 *            the ClientIp address
	 * @return sessionId
	 * @throws WrongCredentialsException
	 *             the wrong credentials exception
	 * @throws DatabaseException
	 *             the database exception
	 * @throws UserNotInDbException 
	 */
	public String login(String username, String password, String ip)
			throws WrongCredentialsException, DatabaseException, UserNotInDbException {
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put(Context.INITIAL_CONTEXT_FACTORY,
				"com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, "ldaps://ldap.fhv.at:636");

		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, "uid=" + username
				+ ",ou=fhv,ou=People,dc=uclv,dc=net");
		env.put(Context.SECURITY_CREDENTIALS, password);

		try {
			DirContext ctx = new InitialDirContext(env);
			ctx.close();

			//Initalise a SessionContainer with the current sessionInformations
			User user = DAOFactory.getInstance().getUserDAO().getUser(username);
			Date date = new Date();
			String session = date.getTime() + "_" + user.hashCode();
			sessions.put(session, new SessionContainer(user, date, ip));

			return session;
		} catch (NamingException e) {
			//Login not possible
			System.out.println(e.getMessage());
			throw new WrongCredentialsException();
		}
	}

	public void logout(String sessionId){
		if(sessions.containsKey(sessionId)){
			sessions.remove(sessionId);
		}
	}
	
	/**
	 * Check sessions.
	 */
	public void checkSessions() {
		for (String key : sessions.keySet()) {
			SessionContainer sc = sessions.get(key);
			if (isSessionExpired(sc.getLogin())) {
				sessions.remove(key);
			}
		}
	}

	/**
	 * Checks if session is expired. A session is active for max 10 hours
	 * 
	 * @param login
	 *            the loginTime
	 * @return the boolean
	 */
	private Boolean isSessionExpired(Date login) {
		GregorianCalendar expirationDate = new GregorianCalendar();
		expirationDate.setTime(login);
		expirationDate.add(Calendar.HOUR_OF_DAY, 10);

		GregorianCalendar actualDate = new GregorianCalendar();

		if (actualDate.after(expirationDate)) {
			return true;
		}
		return false;
	}
	
	public User getUser(String sessionID){
		return ((SessionContainer) sessions.get(sessionID)).getUser();
	}
}